/*
 * Copyright 2013-2014 Ludwig M Brinckmann
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package tau_sadna.tau_map;

import java.io.File;

import org.mapsforge.core.graphics.Bitmap;
import org.mapsforge.core.graphics.Paint;
import org.mapsforge.core.graphics.Style;
import org.mapsforge.core.model.LatLong;
import org.mapsforge.core.model.Point;
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
import org.mapsforge.map.layer.cache.TileCache;
import org.mapsforge.map.layer.overlay.Marker;
import org.mapsforge.map.layer.renderer.TileRendererLayer;
import org.mapsforge.map.model.MapViewPosition;
import org.mapsforge.map.rendertheme.XmlRenderTheme;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.View.MeasureSpec;

/**
 * Utility functions that can be used across different mapsforge based
 * activities.
 */
public final class Utils {
	/**
	 * Compatibility method.
	 * 
	 * @param a
	 *            the current activity
	 */
	@TargetApi(11)
	public static void enableHome(Activity a) {
		if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
			// Show the Up button in the action bar.
			a.getActionBar().setDisplayHomeAsUpEnabled(true);
		}
	}

	/**
	 * Compatibility method.
	 * 
	 * @param view
	 *            the view to set the background on
	 * @param background
	 *            the background
	 */
	@SuppressWarnings("deprecation")
	@TargetApi(16)
	public static void setBackground(View view, Drawable background) {
		if (android.os.Build.VERSION.SDK_INT >= 16) {
			view.setBackground(background);
		} else {
			view.setBackgroundDrawable(background);
		}
	}

	static Marker createMarker(Context c, int resourceIdentifier,
			LatLong latLong) {
		Drawable drawable = c.getResources().getDrawable(resourceIdentifier);
		Bitmap bitmap = AndroidGraphicFactory.convertToBitmap(drawable);
		return new Marker(latLong, bitmap, 0, -bitmap.getHeight() / 2);
	}

	static Paint createPaint(int color, int strokeWidth, Style style) {
		Paint paint = AndroidGraphicFactory.INSTANCE.createPaint();
		paint.setColor(color);
		paint.setStrokeWidth(strokeWidth);
		paint.setStyle(style);
		return paint;
	}

	static Marker createTappableMarker(Context c, int resourceIdentifier,
			LatLong latLong) {
		Drawable drawable = c.getResources().getDrawable(resourceIdentifier);
		Bitmap bitmap = AndroidGraphicFactory.convertToBitmap(drawable);
		return new Marker(latLong, bitmap, 0, -bitmap.getHeight() / 2) {
			@Override
			public boolean onTap(LatLong geoPoint, Point viewPosition,
					Point tapPoint) {
				if (contains(viewPosition, tapPoint)) {
					Log.w("Tapp", "The Marker was touched with onTap: "
							+ this.getLatLong().toString());
					return true;
				}
				return false;
			}
		};
	}

	/*
	 * @param tileCache the cache
	 * 
	 * @param mapViewPosition the position
	 * 
	 * @param layerManager the layer manager
	 * 
	 * @param mapFile the map file
	 * 
	 * @param renderTheme the render theme to use
	 * 
	 * @return the layer
	 */
	static TileRendererLayer createTileRendererLayer(TileCache tileCache,
			MapViewPosition mapViewPosition, File mapFile,
			XmlRenderTheme renderTheme, boolean hasAlpha) {
		TileRendererLayer tileRendererLayer = new TileRendererLayer(tileCache,
				mapViewPosition, hasAlpha, AndroidGraphicFactory.INSTANCE);
		tileRendererLayer.setMapFile(mapFile);
		tileRendererLayer.setXmlRenderTheme(renderTheme);
		tileRendererLayer.setTextScale(1.5f);
		return tileRendererLayer;
	}

	static Bitmap viewToBitmap(Context c, View view) {
		view.measure(MeasureSpec.getSize(view.getMeasuredWidth()),
				MeasureSpec.getSize(view.getMeasuredHeight()));
		view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
		view.setDrawingCacheEnabled(true);
		Drawable drawable = new BitmapDrawable(c.getResources(),
				android.graphics.Bitmap.createBitmap(view.getDrawingCache()));
		view.setDrawingCacheEnabled(false);
		return AndroidGraphicFactory.convertToBitmap(drawable);
	}
	
	public static double distFrom(LatLong latLong1, LatLong latLong2) {
		double lat1 = latLong1.latitude;
		double lng1 = latLong1.longitude;
		double lat2 = latLong2.latitude;
		double lng2 = latLong2.longitude;
		
	    double earthRadius = 3958.75;
	    double dLat = Math.toRadians(lat2-lat1);
	    double dLng = Math.toRadians(lng2-lng1);
	    double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
	               Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
	               Math.sin(dLng/2) * Math.sin(dLng/2);
	    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	    double dist = earthRadius * c;
	
	    int meterConversion = 1609;
	
	    return (dist * meterConversion);
	}
	
	
	public static Bitmap rotateBitmap(Bitmap bitmap, float deg){
		android.graphics.Bitmap androidBitmap = AndroidGraphicFactory.getBitmap(bitmap);
		
		Matrix matrix = new Matrix();
		matrix.postRotate(deg);
		
		// figure out total width and height of new bitmap
		deg = deg % 360;
        double radians = Math.toRadians(deg);
        double sin = Math.abs(Math.sin(radians));
        double cos = Math.abs(Math.cos(radians));
        int newWidth = (int) (androidBitmap.getWidth() * cos + androidBitmap.getHeight() * sin);
        int newHeight = (int) (androidBitmap.getWidth() * sin + androidBitmap.getHeight() * cos);
		
		androidBitmap = android.graphics.Bitmap.createBitmap(androidBitmap, 0, 0, androidBitmap.getWidth(), androidBitmap.getHeight(), matrix, true);
		androidBitmap = android.graphics.Bitmap.createScaledBitmap(androidBitmap, newWidth, newHeight, true);
		Drawable dr = new BitmapDrawable(null, androidBitmap);
		
		Bitmap ret = AndroidGraphicFactory.convertToBitmap(dr);
		
		return ret;
	}
	
	public static Drawable rotateDrawable(Drawable dr, float deg){
		Bitmap bitmap = AndroidGraphicFactory.convertToBitmap(dr);
		bitmap = rotateBitmap(bitmap, deg);
		android.graphics.Bitmap androidBitmap = AndroidGraphicFactory.getBitmap(bitmap);
		dr = new BitmapDrawable(null, androidBitmap);
		
		return dr;
	}
	
	/**
	 * Calc the intersection between the rectangle (0,0,w,h) and the line defined by the two points: (w/2, h/2), (x, y)
	 * (there are 2 intersections but the wanted one is the one closer to (x,y))
	 * Assume w and h are positive
	 * for example: getIntersection(100,100,500,50) = (100,50)
	 */
	public static Point getIntersection(double w, double h, double x, double y) {
		double a = w/2;
		double b = h/2;
		double alpha; //res.x
		double beta;  //res.y
		
		//check corner cases where x==a or y==b
		if(x == a){
			alpha = x;
			if(y>b){
				beta = h;
				return new Point(alpha, beta);
			}
			if(y<b){
				beta = 0;
				return new Point(alpha, beta);
			}
			beta = b;
			return new Point(alpha, beta);
		}
		if(y == b){
			beta = y;
			if(x>a){
				alpha = w;
				return new Point(alpha, beta);
			}
			if(x<a){
				alpha = 0;
				return new Point(alpha, beta);
			}
			alpha = a;
			return new Point(alpha, beta);
		}
		
		if(y >= x*(h/w)){
			if(y >= h-x*(h/w)){//above both diagonals
				beta = h;
				alpha = a +(x-a)*((beta-b)/(y-b));
			}else{
				alpha = 0;
				beta = b +(y-b)*((alpha-a)/(x-a));
			}
		}else{
			if(y >= h-x*(h/w)){
				alpha = w;
				beta = b +(y-b)*((alpha-a)/(x-a));
			}else{//below both diagonals
				beta = 0;
				alpha = a +(x-a)*((beta-b)/(y-b));
			}
		}
		
		
		
		return new Point(alpha, beta);
	}
	
	
	/**
	 * Calc the angle that the vector (x-a, y-b) makes with the positive side of the x-axis, from below
	 * (the result must be between 0 and 360 including 0)
	 * for example: getAngle(0,0,-5,-5) = 135
	 */
	public static double getAngle(double a, double b, double x, double y) {
		return 180*Math.atan2(y-b,x-a)/Math.PI;
	}
	
	
	
	private Utils() {
		throw new IllegalStateException();
	}

}
